package edu.kit.ksri.db.mturk.utils.messaging;

import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;

import com.amazonaws.mturk.requester.HIT;

import edu.kit.ksri.db.mturk.plugins.display.BrowserDisplay;
import edu.kit.ksri.db.mturk.var.StaticInformation;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/
 * HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human
 * -based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research
 * Institute(KSRI) http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert
 * Kern
 * 
 * ---------------------------------------------------------------------------
 * 
 * Purpose: This class collects all {@link Result}s and provides direct access
 * to the number of completed assignments stored within this class. Note: Do not
 * manually change a {@link Result} object still referenced by this class.
 * Remove and read it after the changes.
 */
public class ResultOutputCollector {

	private static final Logger basiclog = Logger.getLogger("basic."
			+ ResultOutputCollector.class);

	/** singleton */
	private static final ResultOutputCollector instance = new ResultOutputCollector();

	/** collects the results sorted by the hit id */
	private final Map<String, Result> results = new HashMap<String, Result>();

	/**
	 * counts the number of assignments included in the {@link Result} objects.
	 * escalated hits are also included
	 */
	private int completedAssignments = 0;

	/** contains the hit ids of all finished hits */
	private static Set<String> completedHits = Collections.synchronizedSet(new HashSet<String>());

	/** the number of requested assignments */
	private int requestedAssignments = 0;

	/** contains the hit ids of all escalated hits */
	private static Set<String> escalatedHits = Collections.synchronizedSet(new HashSet<String>());

	private ResultOutputCollector() {
	}

	/**
	 * 
	 * @return the {@link ResultOutputCollector} singleton
	 */
	public static ResultOutputCollector getInstance() {
		return ResultOutputCollector.instance;
	}

	/**
	 * @param key
	 * @return
	 * @see java.util.Map#containsKey(java.lang.Object)
	 */
	public boolean containsKey(Object key) {
		return this.results.containsKey(key);
	}

	/**
	 * @param value
	 * @return
	 * @see java.util.Map#containsValue(java.lang.Object)
	 */
	public boolean containsValue(Object value) {
		return this.results.containsValue(value);
	}

	/**
	 * do not add assignments to the returned object to avoid inconsistencies
	 * 
	 * @param key
	 * @return
	 * @see java.util.Map#get(java.lang.Object)
	 */
	public Result get(Object key) {
		return this.results.get(key);
	}

	/**
	 * @return
	 * @see java.util.Map#keySet()
	 */
	public Set<String> keySet() {
		return this.results.keySet();
	}

	/**
	 * @param key
	 * @param value
	 * @return
	 * @see java.util.Map#put(java.lang.Object, java.lang.Object)
	 */
	public Result put(String key, Result value) {
		Result r = this.results.put(key, value);
		if (r != null) {
			completedAssignments = completedAssignments
					- r.getAssignments().size() + value.getAssignments().size();
			//if (r.isFinalResult())
			//	completedHits.add(r.getHitId());
		} else
			completedAssignments += value.getAssignments().size();

		if (StaticInformation.isMonitoring)
			BrowserDisplay.update(this.results);

		return r;
	}

	/**
	 * @param key
	 * @return
	 * @see java.util.Map#remove(java.lang.Object)
	 */
	public Result remove(Object key) {
		Result r = this.results.remove(key);
		if (r != null)
			completedAssignments -= r.getAssignments().size();

		if (StaticInformation.isMonitoring)
			BrowserDisplay.update(this.results);

		return r;
	}

	/**
	 * @return
	 * @see java.util.Map#size()
	 */
	public int size() {
		return this.results.size();
	}

	/**
	 * @return the completedAssignments
	 */
	public int getCompletedAssignments() {
		return this.completedAssignments;
	}

	/**
	 * @return the escalatedHits (the respective hit ids)
	 */
	public static String[] getEscalatedHits() {
		String[] res = new String[escalatedHits.size()];
		res = escalatedHits.toArray(res);
		return res;
	}

	/**
	 * @return the completedHits (the respective hit ids)
	 */
	public static String[] getCompletedHits() {
	
		String [] res = completedHits.toArray(new String[completedHits.size()]);
		return res;
	}
	
	/**
	 * @param hitId ID of the completed HIT
	 */
	public static void addCompletedHit(String hitId){
		completedHits.add(hitId);
	}

	/**
	 * @param hitId
	 *            the hit id of a new hit to be escalated
	 */
	public static void escalateHit(String hitId) {
		if (!escalatedHits.contains(hitId))
			escalatedHits.add(hitId);

		if (StaticInformation.isMonitoring)
			BrowserDisplay.update(escalatedHits);
	}

	/**
	 * @return the requestedAssignments
	 */
	public int getRequestedAssignments() {
		return this.requestedAssignments;
	}

	/**
	 * @param requestedAssignments
	 *            the number of requestedAssignments to add
	 */
	public void addRequestedAssignments(int requestedAssignments) {
		this.requestedAssignments += requestedAssignments;
	}

	/**
	 * persists all results into the specified file
	 * 
	 * @param file
	 * @return
	 */
	public boolean exportResults(String folder, HIT[] hits) {
		if (folder.endsWith("/") || folder.endsWith("\\"))
			folder = folder.substring(0, folder.length() - 1);
		try {
			String[] hitids = new String[hits.length];

			for (int i = 0; i < hits.length; i++) {
				hitids[i] = hits[i].getHITId();
			}

			for (Result r : results.values()) {
				int index = ArrayUtils.indexOf(hitids, r.getHitId());

				r.persist(folder, index);
			}
			// -----------------------------------------------------
			PrintWriter pwa = new PrintWriter(folder + "/summaryAll.csv");
			pwa.println(Result.getPersistableStringColumnHeadingsForAnalysis());
			PrintWriter pwc = new PrintWriter(folder + "/summaryCorrect.csv");
			pwc.println(Result.getPersistableStringColumnHeadingsForAnalysis());

			for (Result r : results.values()) {
				int index = ArrayUtils.indexOf(hitids, r.getHitId());

				pwa.print(r.getPersistableStringForAnalysis("analysis", index));
				pwc.print(r.getPersistableStringForAnalysis("correct", index));
			}
			pwa.flush();
			pwa.close();
			pwc.flush();
			pwc.close();
			// -----------------------------------------------------
			basiclog.info("Persisted " + results.size() + " results.");
		} catch (Exception e) {
			basiclog.error("Error while exporting results: " + e);
			return false;
		}
		return true;
	}

}
